﻿Imports System.Collections.Generic
Imports System.ComponentModel.DataAnnotations
Imports System.ComponentModel.DataAnnotations.Schema
Imports System.Data.Entity
Imports System.Security.Claims
Imports System.Security.Principal
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Web
Imports Owin
Imports Microsoft.AspNet.Identity.EntityFramework
Imports Microsoft.AspNet.Identity

' For more information on ASP.NET Identity, please visit http://go.microsoft.com/fwlink/?LinkId=301882
Public Module IdentityConfig
    Public Const LocalLoginProvider As String = "Local"

    Public Property Logins() As UserLoginStoreSync
        Get
            Return m_Logins
        End Get
        Set(value As UserLoginStoreSync)
            m_Logins = value
        End Set
    End Property
    Private m_Logins As UserLoginStoreSync
    Public Property Users() As UserStoreSync
        Get
            Return m_Users
        End Get
        Set(value As UserStoreSync)
            m_Users = value
        End Set
    End Property
    Private m_Users As UserStoreSync
    Public Property Secrets() As UserSecretStoreSync
        Get
            Return m_Secrets
        End Get
        Set(value As UserSecretStoreSync)
            m_Secrets = value
        End Set
    End Property
    Private m_Secrets As UserSecretStoreSync
    Public Property Roles() As RoleStoreSync
        Get
            Return m_Roles
        End Get
        Set(value As RoleStoreSync)
            m_Roles = value
        End Set
    End Property
    Private m_Roles As RoleStoreSync
    Public Property RoleClaimType() As String
        Get
            Return m_RoleClaimType
        End Get
        Set(value As String)
            m_RoleClaimType = value
        End Set
    End Property
    Private m_RoleClaimType As String
    Public Property UserNameClaimType() As String
        Get
            Return m_UserNameClaimType
        End Get
        Set(value As String)
            m_UserNameClaimType = value
        End Set
    End Property
    Private m_UserNameClaimType As String
    Public Property UserIdClaimType() As String
        Get
            Return m_UserIdClaimType
        End Get
        Set(value As String)
            m_UserIdClaimType = value
        End Set
    End Property
    Private m_UserIdClaimType As String
    Public Property ClaimsIssuer() As String
        Get
            Return m_ClaimsIssuer
        End Get
        Set(value As String)
            m_ClaimsIssuer = value
        End Set
    End Property
    Private m_ClaimsIssuer As String

    Public Sub ConfigureIdentity()
        Logins = New UserLoginStoreSync(New EFUserLoginStore(Of UserLogin)(New DbContextFactory(Of IdentityDbContext)()))
        Users = New UserStoreSync(New EFUserStore(Of User)(New DbContextFactory(Of IdentityDbContext)()))
        Secrets = New UserSecretStoreSync(New EFUserSecretStore(Of UserSecret)(New DbContextFactory(Of IdentityDbContext)()))
        Roles = New RoleStoreSync(New EFRoleStore(Of Role, UserRole)(New DbContextFactory(Of IdentityDbContext)()))
        RoleClaimType = ClaimsIdentity.DefaultRoleClaimType
        UserIdClaimType = "http://schemas.microsoft.com/aspnet/userid"
        UserNameClaimType = "http://schemas.microsoft.com/aspnet/username"
        ClaimsIssuer = ClaimsIdentity.DefaultIssuer
    End Sub

    Public Function RemoveUserIdentityClaims(claims As IEnumerable(Of Claim)) As IList(Of Claim)
        Dim filteredClaims As New List(Of Claim)()
      
        For Each c As Claim In claims
            ' Strip out any existing name/nameid claims
            If c.Type <> ClaimTypes.Name AndAlso c.Type <> ClaimTypes.NameIdentifier Then
                filteredClaims.Add(c)
            End If
        Next
        Return filteredClaims
    End Function

    Public Sub AddRoleClaims(roles As IEnumerable(Of String), claims As IList(Of Claim))
        For Each role As String In roles
            claims.Add(New Claim(RoleClaimType, role, ClaimsIssuer))
        Next
    End Sub

    Public Sub AddUserIdentityClaims(userId As String, userName As String, claims As IList(Of Claim))
        claims.Add(New Claim(ClaimTypes.Name, userName, ClaimsIssuer))
        claims.Add(New Claim(UserIdClaimType, userId, ClaimsIssuer))
        claims.Add(New Claim(UserNameClaimType, userName, ClaimsIssuer))
    End Sub

    Public Sub SignIn(context As HttpContext, userClaims As IEnumerable(Of Claim), isPersistent As Boolean)
        context.SignIn(userClaims, UserNameClaimType, RoleClaimType, isPersistent)
    End Sub

    Private Const ProviderNameKey As String = "providerName"
    Public Function GetProviderNameFromRequest(request As HttpRequest) As String
        Return request(ProviderNameKey)
    End Function

    Public Function GetExternalLoginRedirectUrl(accountProvider As String) As String
        Return "/Account/RegisterExternalLogin?" & ProviderNameKey & "=" & accountProvider
    End Function

    <System.Runtime.CompilerServices.Extension> _
    Public Sub SignIn(context As HttpContext, userId As String, claims As IEnumerable(Of Claim), isPersistent As Boolean)
        Dim user As User = TryCast(Users.Find(userId), User)
        If user IsNot Nothing Then
            Dim userClaims As IList(Of Claim) = RemoveUserIdentityClaims(claims)
            AddUserIdentityClaims(userId, user.UserName, userClaims)
            AddRoleClaims(Roles.GetRolesForUser(userId), userClaims)
            SignIn(context, userClaims, isPersistent)
        End If
    End Sub

    Private Function IsLocalUrl(url As String) As Boolean
        Return Not String.IsNullOrEmpty(url) AndAlso ((url(0) = "/"c AndAlso (url.Length = 1 OrElse (url(1) <> "/"c AndAlso url(1) <> "\"c))) OrElse (url.Length > 1 AndAlso url(0) = "~"c AndAlso url(1) = "/"c))
    End Function

    Public Sub RedirectToReturnUrl(returnUrl As String, response As HttpResponse)
        If Not [String].IsNullOrEmpty(returnUrl) AndAlso IsLocalUrl(returnUrl) Then
            response.Redirect(returnUrl)
        Else
            response.Redirect("~/")
        End If
    End Sub
    <System.Runtime.CompilerServices.Extension> _
    Public Async Function GetExternalIdentity(context As HttpContext) As Task(Of ClaimsIdentity)
        Return Await context.Authenticate(Microsoft.Owin.Security.Forms.FormsAuthenticationDefaults.ExternalAuthenticationType)
    End Function

End Module

Public Module IdentityExtensions

    <System.Runtime.CompilerServices.Extension> _
    Public Function GetUserName(identity As IIdentity) As String
        Return identity.Name
    End Function

    <System.Runtime.CompilerServices.Extension> _
    Public Function GetUserId(identity As IIdentity) As String
        Dim ci As ClaimsIdentity = TryCast(identity, ClaimsIdentity)
        If ci IsNot Nothing Then
            Return ci.FindFirstValue(IdentityConfig.UserIdClaimType)
        End If
        Return ""
    End Function

    <System.Runtime.CompilerServices.Extension> _
    Public Function FindFirstValue(identity As ClaimsIdentity, claimType As String) As String
        Dim claim As Claim = identity.FindFirst(claimType)
        If claim IsNot Nothing Then
            Return claim.Value
        End If
        Return Nothing
    End Function

End Module
